﻿/**
 * Peter
 * Created by: Peter Development Team
 *    http://peter.codeplex.com/
 * 
 * GNU General Public License version 2 (GPLv2)
 *    http://peter.codeplex.com/license
 *
 *  This code is provided on an AS IS basis, with no WARRANTIES,
 *  CONDITIONS or GUARANTEES of any kind.
 **/
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.IO;
using System.Reflection;
using Peter.Common;
using Peter.Common.Interfaces;

namespace Peter.Logic
{
   /// <summary>
   /// The model for the plugins.
   /// </summary>
   public class ModelPlugins : IPartImportsSatisfiedNotification, IDisposable, IPluginManager
   {
      private const string PLUGIN_DIR = "Plugins";
      private AggregateCatalog m_Catalog;
      private CompositionContainer m_Container;

      /// <summary>
      /// Initializes a new plugin object.
      /// </summary>
      public void Load ()
      {
         var assem = Assembly.GetExecutingAssembly ();
         if (!Directory.Exists (Path.Combine(CommonUtilities.GetAssemblyDirectory (assem), PLUGIN_DIR)))
            Directory.CreateDirectory (Path.Combine (CommonUtilities.GetAssemblyDirectory (assem), PLUGIN_DIR));

         this.m_Catalog = new AggregateCatalog (new AssemblyCatalog (assem),
            new DirectoryCatalog (PLUGIN_DIR));
         this.m_Container = new CompositionContainer (this.m_Catalog);
         this.m_Container.ComposeParts (this);
      }

      /// <summary>
      /// Gets the list of plugins.
      /// </summary>
      [ImportMany (typeof (IPeterPlugin))]
      public IEnumerable<Lazy<IPeterPlugin, IPeterPluginMetadata>> Plugins { get; set; }

      /// <summary>
      /// Called when a part's imports have been satisfied and it is safe to use.
      /// </summary>
      public void OnImportsSatisfied ()
      {
      }

      /// <summary>
      /// Occurs when the options are loaded.
      /// </summary>
      public void OptionsLoaded ()
      {
         foreach (var plugin in Plugins)
         {
            if (plugin.Metadata.LoadOnStart)
            {
               Action start = plugin.Value.Start;
               start.BeginInvoke (null, null);
            }
         }
      }

      /// <summary>
      /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
      /// </summary>
      /// <filterpriority>2</filterpriority>
      public void Dispose ()
      {
         Dispose (true);
         GC.SuppressFinalize (this);
      }

      /// <summary>
      /// Disposes the object.
      /// </summary>
      /// <param name="disposing">True to release managed resources.</param>
      private void Dispose (bool disposing)
      {
         if (disposing)
         {
            //If we don't check for null, a NullReferenceException is thrown and this is reason of program crash, while closing itself.
            if (this.m_Catalog != null)
            {
               this.m_Catalog.Dispose ();
            }

            if (this.m_Container != null)
            {
               this.m_Container.Dispose ();
            }
         }
      }

      /// <summary>
      /// Composes the given object.
      /// </summary>
      /// <param name="composable">A composable object.</param>
      public void Compose (object composable)
      {
         this.m_Container.ComposeParts (composable);
         var model = composable as ViewModelBase;
         if (model != null)
            model.IsComposed = true;
      }

      /// <summary>
      /// Satisfies the imports of the given object.
      /// </summary>
      /// <param name="item">Item to satisfy imports for.</param>
      public void SatisfyImportsOnce (object item)
      {
         this.m_Container.SatisfyImportsOnce (item);
         var model = item as ViewModelBase;
         if (model != null)
            model.IsComposed = true;
      }
   }
}
